package com.bbcare.followup.plat.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.annotation.Resource;

import com.bbcare.followup.plat.entity.BsWxContent;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.exceptions.JedisConnectionException;

import com.alibaba.fastjson.JSONObject;
import com.bbcare.comm.AppException;
import com.bbcare.comm.Constants;
import com.bbcare.comm.ErrorCode;
import com.bbcare.comm.ToolUtils;
import com.bbcare.department.plat.store.ibatis.IDeparmentBasicDao;
import com.bbcare.followup.plat.entity.Patient;
import com.bbcare.followup.plat.service.IPatientService;
import com.bbcare.followup.plat.service.IWeChatUserService;
import com.bbcare.followup.plat.store.ibatis.IConfCacheDAO;
import com.bbcare.followup.plat.store.ibatis.IContentTemplDAO;
import com.bbcare.followup.plat.store.ibatis.IFollowQueueDAO;
import com.bbcare.followup.plat.store.ibatis.IPatientDAO;
import com.bbcare.followup.plat.store.ibatis.IPatientOperationRecordDAO;
import com.bbcare.followup.plat.store.ibatis.IPatientQueueSchemeDAO;
import com.bbcare.followup.plat.store.ibatis.IPatientTaskContentDAO;
import com.bbcare.followup.plat.store.ibatis.IPatientTaskDAO;

/**
 * @name: app端调用随访系统服务类接口实现
 * @author: DemonLee
 * @createTime: 2017.5.19
 * @description: 随访系统提供给app端调用的相关服务接口实现
 * @modify:
 *
 */
@Service
public class WeChatUserService implements IWeChatUserService {
    private final Log logger = LogFactory.getLog(WeChatUserService.class);

    @Autowired
    private IPatientDAO patientdao;

    @Autowired
    private IFollowQueueDAO queueDao;

    @Autowired
    private IPatientQueueSchemeDAO patientQueueSchemeDao;

    @Autowired
    private IContentTemplDAO contentTemplDao;

    @Autowired
    private IPatientTaskDAO patientTaskDao;

    @Autowired
    private IPatientTaskContentDAO patientTaskConDao;

    @Autowired
    public IConfCacheDAO confCacheDao;

    @Autowired
    private IPatientService patientService;

    @Autowired
    private IDeparmentBasicDao departBasicDao;
    @Autowired
    private IPatientOperationRecordDAO patientOperationRecordDao;
    // @Autowired
    // private IConfCacheRedis confCacheRedis;

    @Resource(name = "jedisPool")
    private JedisPool jedisPool;

	@Override
	public Map<String, Object> getDeptInfoByCode(Map<String, Object> paramMap)
			throws Exception {
		return null;
	}

	@Override
	@Transactional
	public  Map<String, Object> wechatRegister(
			Map<String, Object> servParamMap) throws Exception {
		logger.info("in service wechatRegister");
		Map<String, Object> userMap = new HashMap<String, Object>();

		String babyName = (String)servParamMap.get("babyName");
		String currTime = (String)servParamMap.get("currTime");
		String tenantId = (String)servParamMap.get("tenantId");
		String userType = (String)servParamMap.get("userType");

		Patient patientOld = (Patient)servParamMap.get("patientOld");
		if(null != patientOld && !patientOld.getUserName().equals(babyName)){
			 //修改用户姓名信息 ，加入其它队列时候 不再重新创建基本用户
			 Map<String, Object> qryPtMap = new HashMap<String, Object>();
			 qryPtMap.put("userId", patientOld.getId());
			 qryPtMap.put("updateTime",currTime);
			 qryPtMap.put("userName", babyName);

		     List<String> tenantIdList = new ArrayList<String>();
		     tenantIdList.add(tenantId);
		     tenantIdList.add(Constants.PT_USER_INFO_DEFAULT_TENANT_ID);
		     qryPtMap.put("tenantIdList",tenantIdList);
		     qryPtMap.put("userType",userType);
		     qryPtMap.put("gender", servParamMap.get("gender"));
		     // 修改当前租户id和默认0的基本信息
		     patientdao.updatePatientInfoById(qryPtMap);

		     // 历史表记录
             Map<String, Object> userHisMap = new HashMap<String, Object>();
             userHisMap.put("userId", patientOld.getId());
             userHisMap.put("updateTime", currTime);
             userHisMap.put("updateType", Constants.UPDATE_TYPE_UPDATE_X);
             userHisMap.put("tenantIdList", tenantIdList);
             patientdao.savePatientHisInfo(userHisMap);
		}

		servParamMap.put("sourceType",Constants.PATIENT_SOURSE_TYPE_LEHUI);
		userMap = patientService.registerPatient(servParamMap);
		return userMap;
	}

	 /**
     *  获取access_token
     * @param openId 获取token
     * @param tokenRedisKey
     * @desc 先按传入的key 从缓存取，取不到 则根据openId 从微信端获取
     * @return
     * @throws Exception
     */
	@Override
    public String getAccessToken(String appId,String secret,String redisKey) throws Exception {
//    	logger.info("in service getAccessToken");
        String  accessToken ="";
        Jedis jedis = null;
        String sdUrl =Constants.ACCESS_TOKEN_URL+appId+"&secret="+secret;

        String lockKey ="tokenlocked:" + redisKey; //不同的公众号
        final Random r = new Random();
        String keyValue = System.nanoTime()+""+r.nextInt(100);
        try {
        	//先从redis 获取 ，如果获取为空或已经失效 则从微信获取
        	jedis = jedisPool.getResource();
            jedis.select(Constants.REDIS_DB_INDEX_EVALUATION);

            accessToken =jedis.get(redisKey);
    		//token key不存在  即失效情况下  考虑同步问题
    		int ONE_MILLI_NANOS = 1000000;
    	    //默认超时时间（毫秒）
    		int DEFAULT_TIME_OUT = 1000 * ONE_MILLI_NANOS;
    	    //锁的超时时间（秒），过期删除
    	    int EXPIRE = 30;
    	    long nano = System.nanoTime();
    		while((System.nanoTime() - nano) < DEFAULT_TIME_OUT && StringUtils.isEmpty(accessToken)){
    			//EX seconds – 设置键key的过期时间，单位时秒 PX milliseconds – 设置键key的过期时间，单位时毫秒
    			//NX – 只有键key不存在的时候才会设置key的值 XX – 只有键key存在的时候才会设置key的值
    			String result = jedis.set(lockKey,keyValue,"NX","PX",EXPIRE*1000);
		        if ("OK".equals(result)) {
//    			if (jedis.setnx(lockKey,keyValue) == 1) {//得到锁
//    				jedis.expire(lockKey,EXPIRE);
                  	accessToken =jedis.get(redisKey);//得到锁了  需判断accessToken是否已经被重新赋值
                  	if(StringUtils.isEmpty(accessToken)){
                  		String msg = ToolUtils.getWechatInf(sdUrl,null);
                  		JSONObject jsObject = JSONObject.parseObject(msg);
                  		if (jsObject.containsKey("access_token")) {
  		                 	accessToken = jsObject.getString("access_token");
  		                 	jedis.set(redisKey, accessToken);
  		                 	jedis.expire(redisKey,Constants.ACCESS_TOKEN_EXPIRE_TIME);
                  		}
                  	}

              		//判断是自己锁的 才删除 否则不删
                  	String keyValueGet = jedis.get(lockKey);
                  	if(keyValue.equals(keyValueGet)){
                  		jedis.del(lockKey);
                  	}
                  	break;
          		}else{
          			accessToken =jedis.get(redisKey);
          			if(!StringUtils.isEmpty(accessToken)){
          				break;
          			}
          		}
    			// 短暂休眠，nano避免出现活锁
                Thread.sleep(2,r.nextInt(500));
    		}
        } catch (JedisConnectionException jex) {
            logger.error("redis连接异常..." + jex.getMessage());
            throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_REDIS_CONNECT_ERROR, "Redis缓存连接失败!"));
        } finally {
        	//判断锁是否还在  并且是自己锁的
    		String keyValueGet = jedis.get(lockKey);
        	if(!StringUtils.isEmpty(keyValueGet) && keyValue.equals(keyValueGet)){
        		jedis.del(lockKey);
        	}

            if (null != jedis) {
                try {
                    jedis.close();
                    logger.warn("close redis connection...");
                } catch (Exception ex) {
                    ex.printStackTrace();
                    throw (new AppException(ErrorCode.FOLLOWUP_EXCEPTION_TYPE_REDIS_CONNECT_CLOSE_FAILED,
                            "Redis缓存关闭失败!"));
                }
            }
        }
        return accessToken;
    }

    // 获取用户信息
    public Map<String,Object> getUserInfoByToken(String token,String openId,Map<String,Object> paramMap) throws Exception {
    	Map<String,Object> map = new HashMap<String,Object>();
    	String appId = (String)paramMap.get("appId");
    	String secret = (String)paramMap.get("secret");
    	String redisKey = (String)paramMap.get("redisKey");
        String sdUrl = Constants.GET_USER_URL+token+"&openid="+openId+"&lang=zh_CN";

        String userStr = ToolUtils.getWechatInf(sdUrl,null);
        JSONObject result = JSONObject.parseObject(userStr);

        String errcode = result.getString("errcode");
        if(!StringUtils.isEmpty(errcode) && "40001".equals(errcode)){ //token 失效
        	//先从redis 获取 ，如果获取为空或已经失效 则再取一次 token   暂时不考虑非正常途径获取token导致失效的情况  此情况需人工删除token
            token = this.getAccessToken(appId,secret,redisKey); //
            sdUrl = Constants.GET_USER_URL+token+"&openid="+openId+"&lang=zh_CN";
        	userStr = ToolUtils.getWechatInf(sdUrl,null);
        	result = JSONObject.parseObject(userStr);
        }

        Integer groupId=(Integer) result.get("groupid");
		String nickName=(String) result.get("nickname");
		String headimgurl=(String) result.get("headimgurl");
		String unionid=(String) result.get("unionid");
		map.put("unionid", unionid);
		map.put("groupId", groupId);
		map.put("wxNickName", nickName);
		map.put("wxHeadimgurl", headimgurl);

    	return map;
    }

	@Override
	public List<Map<String, Object>> getComList(Map<String, Object> paramMap)
			throws Exception {
		List<String> list = new ArrayList<String>();
		list.add("maternalPre1214lh");
		list.add("maternalPre0028lh");
		list.add("maternalPre2840lh");
		list.add("maternalDeliverylh");
		list.add("maternalPre42daylh");
		list.add("maternalbasiclh");
		paramMap.put("busiIds",list);
		paramMap.put("opCode",Constants.OP_CODE_1114);
        List<Map<String, Object>> iaOutList = patientOperationRecordDao.selectOperationByCondition(paramMap);
        if(!CollectionUtils.isEmpty(iaOutList)){
        	for(int i = 0;i<iaOutList.size();i++){
        		String opTime=  iaOutList.get(i).get("opTime").toString();
        		iaOutList.get(i).put("isComplete",true);
        		iaOutList.get(i).put("opTime",opTime);
        	}
        }
		return iaOutList;
	}

	@Override
	public Map<String, Object> getWxContent(Map<String, Object> paramMap)
			throws Exception {

		BsWxContent wxContent = confCacheDao.selectWxContent(paramMap);
		Map<String, Object> map = new HashMap<String, Object>();

		map.put("HospitalName", wxContent.getTitle());
		map.put("HospitalImage", wxContent.getHospitalimage());
		map.put("QrCode", wxContent.getQrcode());
		return map;
	}

	@Override
	public Map<String, Object> getOpenId(Map<String, Object> paramMap)
			throws Exception {
		String appId = (String)paramMap.get("appId");
		String secret = (String)paramMap.get("secret");
		String code = (String)paramMap.get("code");
		String sdUrl = Constants.GET_OPENID_CODE+appId+"&secret="+secret+"&code="+code+"&grant_type=authorization_code";
		String userStr = ToolUtils.getWechatInf(sdUrl,null);
		JSONObject result = JSONObject.parseObject(userStr);

		String openId ="";
		if (result.containsKey("openid")) {
			openId = result.getString("openid");
			logger.info("openId:" + openId);
		}
		Map<String,Object> map = new HashMap<String,Object>();
		map.put("openId", openId);
		return map;
	}
}
